package org.yunai.swjg.server.module.quest;

import org.yunai.swjg.server.core.constants.SysMessageConstants;
import org.yunai.swjg.server.module.currency.CurrencyService;
import org.yunai.swjg.server.module.item.Inventory;
import org.yunai.swjg.server.module.item.container.Bag;
import org.yunai.swjg.server.module.item.vo.Item;
import org.yunai.swjg.server.module.player.vo.Player;
import org.yunai.swjg.server.module.quest.condition.IQuestCondition;
import org.yunai.swjg.server.module.quest.template.QuestTemplate;
import org.yunai.swjg.server.module.quest.vo.DoingQuest;
import org.yunai.swjg.server.rpc.message.S_C.S_C_FinishQuestResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_QuestUpdateResp;
import org.yunai.swjg.server.rpc.message.S_C.S_C_SysMessageReq;
import org.yunai.swjg.server.rpc.struct.StQuestCondition;
import org.yunai.swjg.server.rpc.struct.StQuestInfo;
import org.yunai.yfserver.spring.BeanManager;
import org.yunai.yfserver.time.TimeService;
import org.yunai.yfserver.util.Assert;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 任务逻辑基类
 * User: yunai
 * Date: 13-5-8
 * Time: 下午11:20
 */
public abstract class AbstractQuest {

    protected static TimeService timeService;
    protected static CurrencyService currencyService;

    static {
        timeService = BeanManager.getBean(TimeService.class);
        currencyService = BeanManager.getBean(CurrencyService.class);
    }

    /**
     * 任务模版
     */
    private QuestTemplate template;
    /**
     * 前置任务列表
     */
    private List<AbstractQuest> preQuests = new ArrayList<>(1);
    /**
     * 后置任务
     */
    private List<AbstractQuest> nextQuests = new ArrayList<>(1);

    public AbstractQuest(QuestTemplate template) {
        this.template = template;
    }

    public void addPreQuest(AbstractQuest quest) {
        preQuests.add(quest);
    }

    public void addNextQuest(AbstractQuest quest) {
        nextQuests.add(quest);
    }

    public QuestTemplate getTemplate() {
        return template;
    }

    public Integer getId() {
        return getTemplate().getId();
    }

    public List<IQuestCondition> getFinishConditions() {
        return getTemplate().getFinishConditions();
    }

    /**
     * 验证是否能够接受任务<br />
     * <pre>
     *     1. 等级条件是否足够
     *     2. 是否正在进行中
     *     3. 前置任务是否完成
     *     4. 是否满足{@link IQuestCondition}条件
     *     5. 子类实现方法{@link #canAcceptImpl(Player, boolean)}
     * </pre>
     *
     * @param player    玩家信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够接受任务
     */
    private boolean canAccept(Player player, boolean showError) {
        // 等级是否足够
        if (template.getAcceptMinLevel() > player.getLevel()) {
            if (showError) {
                player.message(new S_C_SysMessageReq(SysMessageConstants.QUEST_ACCEPT_LEVEL_NOT_ENOUGH));
            }
            return false;
        }
        // 是否正在进行中的任务
        QuestDiary questDiary = player.getQuestDiary();
        if (questDiary.getDoingQuestInfo(getId()) != null) {
            if (showError) {
                player.message(new S_C_SysMessageReq(SysMessageConstants.QUEST_ACCEPT_DOING));
            }
            return false;
        }
        // 前置任务是否完成
        for (AbstractQuest preQuest : preQuests) {
            if (questDiary.getFinishedQuestInfo(preQuest.getId()) == null) {
                if (showError) {
                    player.message(new S_C_SysMessageReq(SysMessageConstants.QUEST_ACCEPT_PRE_QUEST_NOT_FINISHED));
                }
                return false;
            }
        }
        // 是否满足接受前置条件(IQuestCondition) TODO
        return true;
    }

    /**
     * 子类实现实现是否能够接受任务
     *
     * @param player    玩家信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够接受任务
     */
    protected abstract boolean canAcceptImpl(Player player, boolean showError);

    /**
     * 接受任务
     *
     * @param player 玩家信息
     */
    public void accept(Player player) {
        // 验证
        if (!canAccept(player, true)) {
            return;
        }
        // 创建任务
        DoingQuest doingQuest = DoingQuest.save(player, this, timeService.now());
        player.getQuestDiary().addDoingQuestInfo(doingQuest);
        // 发送任务变化
        player.message(new S_C_QuestUpdateResp(genDoingQuestInfo(player, doingQuest)));
        // TODO 记录日志（等日志服务器）
    }

    /**
     * 验证是否能够取消任务<br />
     * <pre>
     *     1. 任务是否是进行中的
     *     2. 子类实现方法{@link #canCancelImpl(Player, boolean)}
     * </pre>
     *
     * @param player    玩家信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够取消任务
     */
    private boolean canCancel(Player player, boolean showError) {
        // 是否正在进行
        QuestDiary questDiary = player.getQuestDiary();
        if (questDiary.getDoingQuestInfo(getId()) == null) {
            if (showError) {
                player.message(new S_C_SysMessageReq(SysMessageConstants.QUEST_CANCEL_NOT_DOING));
            }
            return false;
        }
        return canCancelImpl(player, showError);
    }

    /**
     * 子类实现实现是否能够取消任务
     *
     * @param player    玩家信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否取消接受任务
     */
    protected abstract boolean canCancelImpl(Player player, boolean showError);

    /**
     * 取消任务
     *
     * @param player 玩家信息
     */
    public void cancel(Player player) {
        // 验证
        if (!canCancel(player, true)) {
            return;
        }
        // 取消任务
        DoingQuest doingQuest = player.getQuestDiary().removeDoingQuestInfo(getId());
        player.delete(doingQuest);
        // 发送任务变化
        StQuestInfo questInfo = genCanAcceptQuestInfo(player);
        if (questInfo != null) {
            player.message(new S_C_QuestUpdateResp(questInfo));
        }
        // TODO 记录日志（等日志服务器）
    }

    /**
     * 验证是否能够完成任务<br />
     * <pre>
     *     1. 是否正在进行中
     *     2. 是否满足{@link IQuestCondition}条件
     *     5. 子类实现方法{@link #canFinishImpl(Player, boolean)}
     * </pre>
     *
     * @param player    玩家信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够接受任务
     */
    private boolean canFinish(Player player, boolean showError) {
        // 验证是否正在进行中
        QuestDiary questDiary = player.getQuestDiary();
        if (questDiary.getDoingQuestInfo(getId()) == null) {
            if (showError) {
                player.message(new S_C_SysMessageReq(SysMessageConstants.QUEST_FINISH_NOT_DOING));
            }
            return false;
        }
        // 验证条件是否满足
        for (IQuestCondition condition : getFinishConditions()) {
            if (!condition.isMeet(player, getId(), showError)) {
                return false;
            }
        }
        return canFinishImpl(player, showError);
    }

    /**
     * 子类实现实现是否能够完成任务
     *
     * @param player    玩家信息
     * @param showError 是否提示错误消息给客户端
     * @return 是否能够完成任务
     */
    protected abstract boolean canFinishImpl(Player player, boolean showError);

    /**
     * 完成任务
     *
     * @param player 玩家信息
     */
    public void finish(Player player) {
        // 验证
        if (!canFinish(player, true)) {
            return;
        }
        // 检查背包
        Inventory inventory = player.getInventory();
        if (!inventory.checkBagSpaceEnough(Bag.BagType.PRIM, template.getBonusItems(), true)) {
            return;
        }
        // 检查金钱是否可以给
        if (!currencyService.canGive(player, template.getBonusCurrency(), template.getBonusCurrencyNum(), true)) {
            return;
        }
        // 执行finishCondition的onFinish
        for (IQuestCondition finishCondition : getFinishConditions()) {
            finishCondition.onFinish(player);
        }
        // 奖励金钱
        currencyService.give(player, template.getBonusCurrency(), template.getBonusCurrencyNum());
        // 奖励经验
        player.addExp(template.getBonusExp());
        // 奖励道具
        List<Item> updateItems = inventory.addItems(Bag.BagType.PRIM, template.getBonusItems());
        inventory.sendModifyMessage(updateItems);
        // 完成任务
        QuestDiary questDiary = player.getQuestDiary();
        DoingQuest doingQuest = questDiary.removeDoingQuestInfo(getId());
        player.delete(doingQuest);
        finishImpl(player, doingQuest);
        // 发送任务完成
        player.message(new S_C_FinishQuestResp(getId()));
        // 重新发送任务列表
        questDiary.noticeInfo();
        // TODO 记录日志（等待日志服务器）
    }

    /**
     * 子类实现，进一步做处理
     *
     * @param player     玩家信息
     * @param doingQuest 进行中的任务信息
     */
    protected abstract void finishImpl(Player player, DoingQuest doingQuest);

    /**
     * 生成正在进行中的任务的任务详细信息
     *
     * @param player     玩家信息
     * @param doingQuest 正在进行中的任务
     * @return 任务详细信息
     */
    public StQuestInfo genDoingQuestInfo(Player player, DoingQuest doingQuest) {
        Assert.isTrue(doingQuest.getQuest() == this);
        StQuestInfo questInfo = new StQuestInfo();
        questInfo.setId(getId());
        if (canFinish(player, false)) {
            questInfo.setStatus(QuestDef.Status.CAN_FINISHED.getValue());
        } else {
            questInfo.setStatus(QuestDef.Status.ACCEPTED.getValue());
        }
        questInfo.setFinishConditions(genFinishConditions(player));
        return questInfo;
    }

    /**
     * 生成可以接受的任务的任务详细信息
     *
     * @param player 玩家信息
     * @return 任务详细信息
     */
    public StQuestInfo genCanAcceptQuestInfo(Player player) {
        if (!canAccept(player, false)) {
            return null;
        }
        StQuestInfo questInfo = new StQuestInfo();
        questInfo.setId(getId());
        questInfo.setStatus(QuestDef.Status.CAN_ACCEPTED.getValue());
        questInfo.setFinishConditions(genFinishConditions(player));
        return questInfo;
    }

    /**
     * 获得任务完成条件数组
     *
     * @param player 玩家信息
     * @return 任务完成条件数组
     */
    public final List<StQuestCondition> genFinishConditions(Player player) {
        AbstractQuest quest = player.getQuestDiary().getDoingQuest(getId());
        if (quest == null) {
            return Collections.emptyList();
        }
        List<StQuestCondition> finishConditions = new ArrayList<>(quest.getFinishConditions().size());
        for (IQuestCondition cond : quest.getFinishConditions()) {
            finishConditions.add(cond.genFinishCondition(player, getId()));
        }
        return finishConditions;
    }

    /**
     * 创建任务对象简单工厂
     */
    public static class Factory {

        /**
         * 创建任务对象
         *
         * @param template 模版
         * @return 任务对象
         */
        public static AbstractQuest create(QuestTemplate template) {
            switch (template.getType()) {
                case MAIN:
                case BRANCH:
                    return new CommonQuest(template);
                default:
                    throw new IllegalArgumentException("暂时支持该任务类型");
            }
        }
    }


}
